home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / Synthesizer Source / Synthesizer Folder / Level 0 Macintosh 20Jul94 / Network.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-01  |  40.7 KB  |  1,301 lines  |  [TEXT/KAHL]

  1. /* Network.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Audit.h"
  21. #include "Debug.h"
  22. #include "Definitions.h"
  23.  
  24. #pragma options(pack_enums)
  25. #include <GestaltEqu.h>
  26. #include <PPCToolbox.h>
  27. #include <Script.h>
  28. #include <Errors.h>
  29. #pragma options(!pack_enums)
  30.  
  31. #include "Network.h"
  32. #include "Memory.h"
  33. #include "EventLoop.h"
  34. #include "Array.h"
  35.  
  36.  
  37. /* this is the AppleTalk PPCToolBox version of the Network module */
  38.  
  39.  
  40. /* since this system can only handle one outstanding requested connection at */
  41. /* a time, we might have to try several times to establish a connection.  these */
  42. /* specify how many times to try and how long to wait between each one. */
  43. #define MAXNUMTRIES (10)
  44. #define TRYDELAY (1.0) /* in seconds */
  45.  
  46. #define DEFAULTTYPE "PPCToolBox"
  47. #define DEFAULTTYPELENGTH (10)
  48.  
  49. /* size of a single buffer record */
  50. #define BUFFERSIZE (256)
  51.  
  52. typedef enum
  53.     {
  54.         eAwaitingConnection EXECUTE(= -8982),
  55.         eConnectionPending
  56.     } PortStates;
  57.  
  58. struct PortIDType
  59.     {
  60.         PPCPortRefNum                    ThePortRefnum;
  61.         PortStates                        PortState;
  62.         PPCInformPBRec                ThePPCInformPBRec;
  63.         PPCPortRec                        ThePPCPortRec;
  64.         PPCPortRec                        RemotePortName; /* temporary buffer for it */
  65.         LocationNameRec                RemoteLocationName; /* temporary buffer */
  66.     };
  67.  
  68. typedef struct BufferRec
  69.     {
  70.         struct BufferRec*            Next;
  71.         long                                    NumBytes;
  72.         char                                    Buffer[BUFFERSIZE];
  73.     } BufferRec;
  74.  
  75. typedef enum
  76.     {
  77.         eWriteIdle EXECUTE(= -4152),
  78.         eWriteInProgress,
  79.         eWriteFinished
  80.     } SessionWriteStates;
  81.  
  82. typedef enum
  83.     {
  84.         eReadIdle EXECUTE(= -28874),
  85.         eReadInProgress,
  86.         eReadFinished
  87.     } SessionReadStates;
  88.  
  89. typedef enum
  90.     {
  91.         eSessionNormal EXECUTE(= -9887),
  92.         eSessionDataArrived,
  93.         eSessionClosed
  94.     } SessionStates;
  95.  
  96. struct SessionIDType
  97.     {
  98.         PPCSessRefNum                    SessionRefnum;
  99.         PortIDType*                        Port;
  100.         SessionWriteStates        WriteState;
  101.         SessionReadStates            ReadState;
  102.         SessionStates                    OverallState;
  103.         BufferRec*                        SendHead;
  104.         BufferRec*                        SendTail;
  105.         BufferRec*                        SendInProgress; /* valid if WriteState != eWriteIdle */
  106.         BufferRec*                        ReceiveHead;
  107.         BufferRec*                        ReceiveTail;
  108.         BufferRec*                        ReceiveInProgress; /* valid if ReadState != eReadIdle */
  109.         PPCReadPBRec                    ThePPCReadPBRec;
  110.         PPCWritePBRec                    ThePPCWritePBRec;
  111.         PPCPortRec                        RemotePortName; /* probably not needed */
  112.         LocationNameRec                RemoteLocationName; /* NBP name of remote machine */
  113.     };
  114.  
  115.  
  116. /* maintains the status of this module for debugging */
  117. EXECUTE(static MyBoolean                    Initialized = False;)
  118.  
  119. /* list of network ports.  NIL means the port slot is available.  the length */
  120. /* is determined by the heap block size. */
  121. static ArrayRec*                                    PortArray;
  122.  
  123. /* list of sessions.  NIL means session slot is available... */
  124. static ArrayRec*                                    SessionArray;
  125.  
  126. /* This system allows you to make an outgoing connection without a port.  The */
  127. /* Macintosh requires all connections to be made through ports, so we provide */
  128. /* a "system port" through which all outgoing connections are made. */
  129. static PortIDType*                                AppleTalkSystemPort;
  130.  
  131. /* session scan counter.  this is used so that all of the sessions are checked */
  132. /* fairly instead of favoring ones that are near the beginning of the list */
  133. static long                                                SessionScan;
  134.  
  135.  
  136. /* utility function prototypes */
  137. static void                    ReinstallPPCInform(PortIDType* Port);
  138. static MyBoolean        DecodeMachineName(char* MachineStr, char ObjStr[32],
  139.                                             char TypeStr[32], char ZoneStr[32]);
  140. static void                    UpdateRead(SessionIDType* Session);
  141. static void                    UpdateWrite(SessionIDType* Session);
  142. static pascal void    MyPPCInformCompletionRoutine(PPCInformPBRec* PB);
  143. static pascal void    MyPPCReadCompletionRoutine(PPCParamBlockRec* PB);
  144. static pascal void    MyPPCWriteCompletionRoutine(PPCParamBlockRec* PB);
  145.  
  146.  
  147. /* initialize the network interface.  the user calls this.  it is not called from */
  148. /* the standard Level 0 initialization routine because not all programs need */
  149. /* networking.  Could return eNetNoError, eNetNoMemory, eNetNetworkNotAvailable, */
  150. /* eNetCouldntInitNet, or eNetUnknownError */
  151. NetErrors                        InitializeNetwork(void)
  152.     {
  153.         OSErr                            Error;
  154.         long                            Result;
  155.         NetErrors                    ReturnValue;
  156.         unsigned long            PortIDScan;
  157.  
  158.         ERROR(Initialized,PRERR(ForceAbort,"InitializeNetwork:  already initialized"));
  159.         EXECUTE(Initialized = True;)
  160.         Error = Gestalt(gestaltPPCToolboxAttr,&Result);
  161.         if (Error != noErr)
  162.             {
  163.                 ReturnValue = eNetNetworkNotAvailable;
  164.              FailurePoint1:
  165.                 EXECUTE(Initialized = False;)
  166.                 return ReturnValue;
  167.             }
  168.         if ((Result & gestaltPPCSupportsRealTime) == 0)
  169.             {
  170.                 /* not initialized.  try initializing */
  171.                 Error = PPCInit();
  172.                 if (Error != noErr)
  173.                     {
  174.                         ReturnValue = eNetCouldntInitNet;
  175.                         goto FailurePoint1;
  176.                     }
  177.                 Error = Gestalt(gestaltPPCToolboxAttr,&Result);
  178.             }
  179.         /* don't know what to do in these cases */
  180.         if ((Result & gestaltPPCSupportsOutGoing) == 0)
  181.             {
  182.             }
  183.         if ((Result & gestaltPPCSupportsIncoming) == 0)
  184.             {
  185.             }
  186.         /* allocate arrays for holding session and port records */
  187.         PortArray = NewArray();
  188.         if (PortArray == NIL)
  189.             {
  190.                 ReturnValue = eNetNoMemory;
  191.              FailurePoint2:
  192.                 goto FailurePoint1;
  193.             }
  194.         SessionArray = NewArray();
  195.         if (SessionArray == NIL)
  196.             {
  197.                 ReturnValue = eNetNoMemory;
  198.              FailurePoint3:
  199.                 DisposeArray(PortArray);
  200.                 goto FailurePoint2;
  201.             }
  202.         /* initialize the session counter for NetUpdate */
  203.         SessionScan = 0;
  204.         /* trying to obtain a port for outgoing connections.  port names must be unique */
  205.         /* and since there may be several programs using this package running on the */
  206.         /* same computer, we need to search for an available port to work out of. */
  207.         for (PortIDScan = 0xffffffff; PortIDScan > 0; PortIDScan -= 1)
  208.             {
  209.                 char*                            PortNameTemp;
  210.                 int                                Scan;
  211.  
  212.                 PortNameTemp = AllocPtrCanFail(8,"SysPortName");
  213.                 if (PortNameTemp == NIL)
  214.                     {
  215.                         ReturnValue = eNetNoMemory;
  216.                         goto FailurePoint3;
  217.                     }
  218.                 for (Scan = 0; Scan < 8; Scan += 1)
  219.                     {
  220.                         PortNameTemp[Scan] = 'a' + ((PortIDScan >> (Scan * 4)) & 15);
  221.                     }
  222.                 /* try to open the port */
  223.                 ReturnValue = NetListenAtPort(PortNameTemp,&AppleTalkSystemPort,eNetAppleTalk);
  224.                 ReleasePtr(PortNameTemp);
  225.                 /* see if the port worked */
  226.                 switch (ReturnValue)
  227.                     {
  228.                         default:
  229.                             EXECUTE(PRERR(ForceAbort,
  230.                                 "InitializeNetwork:  bad value from NetListenAtPort"));
  231.                             break;
  232.                         case eNetNoError:
  233.                             goto SystemPortAllocatedPoint;
  234.                         case eNetNoMemory:
  235.                             /* value of ReturnValue stays the same */
  236.                             /* ReturnValue = eNetNoMemory; */
  237.                          FailurePoint4:
  238.                             DisposeArray(SessionArray);
  239.                             goto FailurePoint3;
  240.                         case eNetPortInUse:
  241.                             break; /* loop again */
  242.                         case eNetBadPortString:
  243.                             EXECUTE(PRERR(ForceAbort,"InitializeNetwork:  bad port string"));
  244.                             break;
  245.                         case eNetUnknownError:
  246.                             /* value of ReturnValue stays the same */
  247.                             /* ReturnValue = eNetUnknownError; */
  248.                             goto FailurePoint4;
  249.                             break;
  250.                     }
  251.             }
  252.         /* fall through:  couldn't allocate any system ports */
  253.         ReturnValue = eNetUnknownError;
  254.         goto FailurePoint4;
  255.         /* jump here if all is good */
  256.      SystemPortAllocatedPoint:
  257.         return eNetNoError;
  258.     }
  259.  
  260.  
  261. /* discard any pending data, close any open connections, and clean internal data */
  262. void                                ShutdownNetwork(void)
  263.     {
  264.         long                            Scan;
  265.         long                            Limit;
  266.  
  267.         ERROR(!Initialized,PRERR(ForceAbort,"ShutdownNetwork:  not initialized"));
  268.         /* first, close all sessions */
  269.         Limit = ArrayGetLength(SessionArray);
  270.         for (Scan = 0; Scan < Limit; Scan += 1)
  271.             {
  272.                 SessionIDType*        Session;
  273.  
  274.                 Session = (SessionIDType*)ArrayGetElement(SessionArray,Scan);
  275.                 EXECUTE(PRERR(AllowResume,"ShutdownNetwork:  session still open"));
  276.                 NetCloseSession(Session);
  277.             }
  278.         /* now close all open ports, including the system port */
  279.         NetTerminatePortAndSessions(AppleTalkSystemPort); /* technically not needed */
  280.         Limit = ArrayGetLength(PortArray);
  281.         for (Scan = 0; Scan < Limit; Scan += 1)
  282.             {
  283.                 PortIDType*                Port;
  284.  
  285.                 Port = (PortIDType*)ArrayGetElement(PortArray,Scan);
  286.                 EXECUTE(PRERR(AllowResume,"ShutdownNetwork:  found a port that's still open"));
  287.                 NetTerminatePortAndSessions(Port);
  288.             }
  289.         /* now release memory occupied by arrays */
  290.         DisposeArray(PortArray);
  291.         DisposeArray(SessionArray);
  292.         EXECUTE(Initialized = False;)
  293.     }
  294.  
  295.  
  296. /* Network update routine.  It handles periodic update tasks and should be called */
  297. /* frequently (preferably in the main event loop next to GetAnEvent). */
  298. NetEvents                        NetUpdate(SessionIDType** SessionNumber)
  299.     {
  300.         OSErr                            Error;
  301.         long                            Scan;
  302.         long                            Limit;
  303.         long                            OldSessionScan;
  304.         NetEvents                    ReturnValue;
  305.  
  306.         ERROR(SessionNumber == NIL,PRERR(ForceAbort,"NetUpdate:  SessionNumber == NIL"));
  307.         ERROR(!Initialized,PRERR(ForceAbort,"NetUpdate:  not initialized"));
  308.         /* look for PPCInforms that completed */
  309.         Limit = ArrayGetLength(PortArray);
  310.         for (Scan = 0; Scan < Limit; Scan += 1)
  311.             {
  312.                 PortIDType*                Port;
  313.  
  314.                 Port = (PortIDType*)ArrayGetElement(PortArray,Scan);
  315.                 if (Port->PortState == eConnectionPending)
  316.                     {
  317.                         SessionIDType*        Session;
  318.  
  319.                         /* return an event indicating the new session and reset the PPCInform */
  320.                         Session = (SessionIDType*)AllocPtrCanFail(sizeof(SessionIDType),"Session");
  321.                         if ((Session == NIL) || (Port == AppleTalkSystemPort))
  322.                             {
  323.                                 PPCEndPBRec                ThePPCEndPBRec;
  324.  
  325.                                 /* if we are out of memory, then kill the session */
  326.                                 /* we also do this if the connection was established on the */
  327.                                 /* system port */
  328.                              NewSessionFailurePoint1:
  329.                                 ThePPCEndPBRec.sessRefNum = Port->ThePPCInformPBRec.sessRefNum;
  330.                                 Error = PPCEnd(&ThePPCEndPBRec,False/*sync*/);
  331.                                 ERROR(Error != noErr,PRERR(AllowResume,"NetUpdate:  PPCEnd != noErr"));
  332.                                 ReinstallPPCInform(Port); /* this resets PortState */
  333.                                 goto NextPortPoint;
  334.                             }
  335.                         if (!ArrayAppendElement(SessionArray,Session))
  336.                             {
  337.                                 goto NewSessionFailurePoint1;
  338.                             }
  339.                         /* initialize all of the fields of the session record */
  340.                         Session->SessionRefnum = Port->ThePPCInformPBRec.sessRefNum;
  341.                         Session->Port = Port;
  342.                         Session->WriteState = eWriteIdle;
  343.                         Session->ReadState = eReadIdle;
  344.                         Session->OverallState = eSessionNormal;
  345.                         Session->SendHead = NIL;
  346.                         Session->SendTail = NIL;
  347.                         Session->SendInProgress = NIL;
  348.                         Session->ReceiveHead = NIL;
  349.                         Session->ReceiveTail = NIL;
  350.                         Session->ReceiveInProgress = NIL;
  351.                         /* initiate the first PPC read operation */
  352.                         UpdateRead(Session);
  353.                         /* copy over the port and location names of the remote session */
  354.                         Session->RemotePortName = Port->RemotePortName;
  355.                         Session->RemoteLocationName = Port->RemoteLocationName;
  356.                         ReinstallPPCInform(Port); /* this resets PortState */
  357.                         /* return session number for caller */
  358.                         *SessionNumber = Session;
  359.                         return eNetEventNewSession;
  360.                     }
  361.                 /* jump here after killing a session that we couldn't handle so that */
  362.                 /* we can check the next port. */
  363.              NextPortPoint:
  364.                 ;
  365.             }
  366.         /* now do session scans */
  367.         ReturnValue = eNetEventNone;
  368.         Limit = ArrayGetLength(SessionArray);
  369.         if (Limit > 0)
  370.             {
  371.                 OldSessionScan = SessionScan;
  372.                 if (OldSessionScan > Limit - 1)
  373.                     {
  374.                         /* this prevents us from getting stuck in an infinite loop if someone */
  375.                         /* removed an array element and left SessionScan beyond the end of the array */
  376.                         OldSessionScan = 0;
  377.                     }
  378.                 do
  379.                     {
  380.                         SessionIDType*            Session;
  381.  
  382.                         /* we increment first so that we don't dwell on hot sessions */
  383.                         SessionScan += 1;
  384.                         if (SessionScan >= Limit)
  385.                             {
  386.                                 SessionScan = 0;
  387.                             }
  388.                         Session = (SessionIDType*)ArrayGetElement(SessionArray,SessionScan);
  389.                         /* check to see if port is closed */
  390.                         if (Session->OverallState == eSessionClosed)
  391.                             {
  392.                                 *SessionNumber = Session;
  393.                                 return eNetEventSessionClosed;
  394.                             }
  395.                         /* check to see if write needs to be retriggered */
  396.                         if (Session->WriteState != eWriteInProgress)
  397.                             {
  398.                                 if (Session->WriteState == eWriteFinished)
  399.                                     {
  400.                                         ReturnValue = eNetEventInternal;
  401.                                     }
  402.                                 UpdateWrite(Session);
  403.                             }
  404.                         /* check to see if read needs to be retriggered */
  405.                         if (Session->ReadState != eReadInProgress)
  406.                             {
  407.                                 if (Session->ReadState == eReadFinished)
  408.                                     {
  409.                                         ReturnValue = eNetEventInternal;
  410.                                     }
  411.                                 UpdateRead(Session);
  412.                             }
  413.                         /* check to see if there is new data */
  414.                         if (Session->OverallState == eSessionDataArrived)
  415.                             {
  416.                                 Session->OverallState = eSessionNormal;
  417.                                 *SessionNumber = Session;
  418.                                 return eNetEventDataIncoming;
  419.                             }
  420.                     } while (SessionScan != OldSessionScan);
  421.             }
  422.         return ReturnValue;
  423.     }
  424.  
  425.  
  426. /* local utility routine for installing an asynchronous PPC callback */
  427. static void                    ReinstallPPCInform(PortIDType* Port)
  428.     {
  429.         OSErr                            Error;
  430.  
  431.         Port->ThePPCInformPBRec.ioCompletion = (ProcPtr)&MyPPCInformCompletionRoutine;
  432.         Port->ThePPCInformPBRec.portRefNum = Port->ThePortRefnum;
  433.         Port->ThePPCInformPBRec.autoAccept = True;
  434.         /* provide a place for identifying the remote port */
  435.         Port->ThePPCInformPBRec.portName = &(Port->RemotePortName);
  436.         /* provide a place for identifying the remote machine */
  437.         Port->ThePPCInformPBRec.locationName = &(Port->RemoteLocationName);
  438.         /* we don't support user names */
  439.         Port->ThePPCInformPBRec.userName = NIL;
  440.         Port->PortState = eAwaitingConnection; /* BEFORE the call */
  441.         Error = PPCInform(&(Port->ThePPCInformPBRec),True/*async*/);
  442.         ERROR(Error != noErr,PRERR(AllowResume,"ReinstallPPCInform:  PPCInform != noErr"));
  443.     }
  444.  
  445.  
  446. /* Listen at the specified port.  Returns the reference number of the port being */
  447. /* listened at.  PortString is a non-null-terminated heap block containing the string */
  448. /* identifying the port.  The port string format depends on the underlying network */
  449. /* scheme.  For instance, TCP/IP would be an integer between 128 and 9999. */
  450. /* for AppleTalk (NBP), it is a valid NBP name.  Could return eNetNoError, */
  451. /* eNetNoMemory, eNetPortInUse, eNetBadPortString, eNetUnknownError, or */
  452. /* eNetProtocolNotSupported. */
  453. NetErrors                        NetListenAtPort(char* PortString, PortIDType** PortOut,
  454.                                             NetworkTypes WhichNetwork)
  455.     {
  456.         PPCOpenPBRec            ThePPCOpenPBRec;
  457.         long                            PortStringLength;
  458.         PortIDType*                Port;
  459.         OSErr                            Error;
  460.         NetErrors                    ReturnValue;
  461.  
  462.         ERROR(!Initialized,PRERR(ForceAbort,"NetListenAtPort:  not initialized"));
  463.         CheckPtrExistence(PortString);
  464.         ERROR(PortOut == NIL,PRERR(ForceAbort,"NetListenAtPort:  PortOut is NIL"));
  465.         /* verify network connection requested */
  466.         switch (WhichNetwork)
  467.             {
  468.                 case eNetDefault:
  469.                 case eNetAppleTalk:
  470.                     break;
  471.                 case eNetTCP:
  472.                     return eNetProtocolNotSupported;
  473.                 default:
  474.                     EXECUTE(PRERR(ForceAbort,"NetListenAtPort:  bad protocol specified"));
  475.                     break;
  476.             }
  477.         /* make a place for it */
  478.         Port = (PortIDType*)AllocPtrCanFail(sizeof(PortIDType),"PortIDType");
  479.         if (Port == NIL)
  480.             {
  481.                 ReturnValue = eNetNoMemory;
  482.              FailurePoint1:
  483.                 return ReturnValue;
  484.             }
  485.         if (!ArrayAppendElement(PortArray,Port))
  486.             {
  487.                 ReturnValue = eNetNoMemory;
  488.              FailurePoint2:
  489.                 ReleasePtr((char*)Port);
  490.                 goto FailurePoint1;
  491.             }
  492.         /* initialize the port record */
  493.         Port->ThePPCPortRec.nameScript = smRoman;
  494.         PortStringLength = PtrSize(PortString);
  495.         if (PortStringLength > 31)
  496.             {
  497.                 ReturnValue = eNetBadPortString;
  498.              FailurePoint3:
  499.                 ArrayDeleteElement(PortArray,ArrayFindElement(PortArray,Port));
  500.                 goto FailurePoint2;
  501.             }
  502.         CopyData(PortString,(char*)&(Port->ThePPCPortRec.name[1]),PortStringLength);
  503.         Port->ThePPCPortRec.name[0] = PortStringLength; /* stupid pascal strings */
  504.         Port->ThePPCPortRec.portKindSelector = ppcByString;
  505.         CopyData(DEFAULTTYPE,(char*)&(Port->ThePPCPortRec.u.portTypeStr[1]),
  506.             DEFAULTTYPELENGTH);
  507.         Port->ThePPCPortRec.u.portTypeStr[0] = DEFAULTTYPELENGTH;
  508.         /* initialize the Openrec */
  509.         ThePPCOpenPBRec.serviceType = ppcServiceRealTime;
  510.         ThePPCOpenPBRec.resFlag = 0;
  511.         ThePPCOpenPBRec.portName = &(Port->ThePPCPortRec);
  512.         ThePPCOpenPBRec.locationName = NIL;
  513.         ThePPCOpenPBRec.networkVisible = True;
  514.         /* try to open it */
  515.         Error = PPCOpen(&ThePPCOpenPBRec,False/*synchronous*/);
  516.         switch (Error)
  517.             {
  518.                 case noErr:
  519.                     Port->ThePortRefnum = ThePPCOpenPBRec.portRefNum;
  520.                     break; /* continue on to the next phase */
  521.                 case badLocNameErr:
  522.                     ReturnValue = eNetBadPortString;
  523.                     goto FailurePoint3;
  524.                 case portNameExistsErr:
  525.                 case nbpDuplicate:
  526.                     ReturnValue = eNetPortInUse;
  527.                     goto FailurePoint3;
  528.                 default:
  529.                     ReturnValue = eNetUnknownError;
  530.                     goto FailurePoint3;
  531.             }
  532.         /* indicate that the callback is out there */
  533.         Port->PortState = eAwaitingConnection;
  534.         /* enable listen callback */
  535.         ReinstallPPCInform(Port);
  536.         /* put reference number out for caller */
  537.         *PortOut = Port;
  538.         return eNetNoError;
  539.     }
  540.  
  541.  
  542. /* Stop listening at a port and let the OS use it for someone else.  Sessions */
  543. /* established through this port are terminated. */
  544. void                                NetTerminatePortAndSessions(PortIDType* Port)
  545.     {
  546.         OSErr                            Error;
  547.         PPCClosePBRec            TheCloseRec;
  548.         long                            Scan;
  549.         long                            Limit;
  550.  
  551.         ERROR(!Initialized,PRERR(ForceAbort,
  552.             "NetTerminatePortAndSessions:  not initialized"));
  553.         CheckPtrExistence(Port);
  554.         /* close any sessions registered through this port */
  555.         Limit = ArrayGetLength(SessionArray);
  556.         Scan = 0;
  557.         while (Scan < Limit)
  558.             {
  559.                 SessionIDType*            Session;
  560.  
  561.                 Session = (SessionIDType*)ArrayGetElement(SessionArray,Scan);
  562.                 if (Session->Port == Port)
  563.                     {
  564.                         /* clean up our local data structures */
  565.                         NetCloseSession(Session);
  566.                         /* decrement limit, since we just dropped current element from array */
  567.                         Limit -= 1;
  568.                     }
  569.                  else
  570.                     {
  571.                         /* only increment this if we didn't delete the element */
  572.                         Scan += 1;
  573.                     }
  574.             }
  575.         /* close the port itself */
  576.         TheCloseRec.portRefNum = Port->ThePortRefnum;
  577.         Error = PPCClose(&TheCloseRec,False/*synchronous*/);
  578.         ERROR(Error != noErr,PRERR(AllowResume,
  579.             "NetTerminatePortAndSessions:  return from PPCClose != noErr"));
  580.         ArrayDeleteElement(PortArray,ArrayFindElement(PortArray,Port));
  581.         ReleasePtr((char*)Port);
  582.     }
  583.  
  584.  
  585. /* Open a session to another (or the same) machine.  Returns a session number in */
  586. /* *SessionOut.  PortString is the remote port to connect to, as described above, */
  587. /* and MachineString is a machine string determined by the network stack.  For */
  588. /* instance, TCP/IP would support standard a.b.c.d or machine.zone.domain format. */
  589. /* The Macintosh uses machine:type@zone.  Could return eNetNoError, eNetNoMemory, */
  590. /* eNetBadPortString, eNetBadMachineString, eNetMachineUnknown, eNetUnknownError, */
  591. /* eNetConnectRefused, or eNetProtocolNotSupported. */
  592. NetErrors                        NetOpenSession(char* PortString, char* MachineString,
  593.                                             SessionIDType** SessionOut, NetworkTypes WhichNetwork)
  594.     {
  595.         PPCStartPBRec            ThePPCStartPBRec;
  596.         OSErr                            Error;
  597.         long                            PortNameLength;
  598.         SessionIDType*        Session;
  599.         NetErrors                    ReturnValue;
  600.         long                            TryCounter;
  601.         unsigned char            UserNamePlace[32];
  602.  
  603.         ERROR(!Initialized,PRERR(ForceAbort,"NetOpenSession:  not initialized"));
  604.         CheckPtrExistence(PortString);
  605.         CheckPtrExistence(MachineString);
  606.         ERROR(SessionOut == NIL,PRERR(ForceAbort,"NetOpenSession:  SessionOut == NIL"));
  607.         /* verify network connection requested */
  608.         switch (WhichNetwork)
  609.             {
  610.                 case eNetDefault:
  611.                 case eNetAppleTalk:
  612.                     break;
  613.                 case eNetTCP:
  614.                     return eNetProtocolNotSupported;
  615.                 default:
  616.                     EXECUTE(PRERR(ForceAbort,"NetListenAtPort:  bad protocol specified"));
  617.                     break;
  618.             }
  619.         /* allocate a place for it */
  620.         Session = (SessionIDType*)AllocPtrCanFail(sizeof(SessionIDType),"SessionIDType");
  621.         if (Session == NIL)
  622.             {
  623.                 ReturnValue = eNetNoMemory;
  624.              FailurePoint1:
  625.                 return ReturnValue;
  626.             }
  627.         if (!ArrayAppendElement(SessionArray,Session))
  628.             {
  629.                 ReturnValue = eNetNoMemory;
  630.              FailurePoint2:
  631.                 ReleasePtr((char*)Session);
  632.                 goto FailurePoint1;
  633.             }
  634.         /* initialize the ppc record */
  635.         EXECUTE(ThePPCStartPBRec.ioCompletion = (ProcPtr)0x81818181;)
  636.         ThePPCStartPBRec.portRefNum = AppleTalkSystemPort->ThePortRefnum;
  637.         ThePPCStartPBRec.serviceType = ppcServiceRealTime;
  638.         ThePPCStartPBRec.resFlag = 0;
  639.         /* see if this connection is for this machine or another */
  640.         PortNameLength = PtrSize(PortString);
  641.         if (PortNameLength > 31)
  642.             {
  643.                 ReturnValue = eNetBadPortString;
  644.              FailurePoint3:
  645.                 ArrayDeleteElement(SessionArray,ArrayFindElement(SessionArray,Session));
  646.                 goto FailurePoint2;
  647.             }
  648.         CopyData(PortString,(char*)&(Session->RemotePortName.name[1]),PortNameLength);
  649.         Session->RemotePortName.name[0] = PortNameLength;
  650.         Session->RemotePortName.nameScript = smRoman;
  651.         Session->RemotePortName.portKindSelector = ppcByString;
  652.         CopyData(DEFAULTTYPE,(char*)&(Session->RemotePortName.u.portTypeStr[1]),
  653.             DEFAULTTYPELENGTH);
  654.         Session->RemotePortName.u.portTypeStr[0] = DEFAULTTYPELENGTH;
  655.         ThePPCStartPBRec.portName = &(Session->RemotePortName);
  656.         if ((PtrSize(MachineString) != 9) && (PtrSize(MachineString) != 0))
  657.             {
  658.                 char                            MyNBPObjectStr[32];
  659.                 char                            MyNBPTypeStr[32];
  660.                 char                            MyNBPZoneStr[32];
  661.  
  662.                 /* remote machine specified */
  663.              RemoteName:
  664.                 if (!DecodeMachineName(MachineString,MyNBPObjectStr,MyNBPTypeStr,MyNBPZoneStr))
  665.                     {
  666.                         ReturnValue = eNetBadMachineString;
  667.                         goto FailurePoint2;
  668.                     }
  669.                 CopyData(&(MyNBPObjectStr[0]),(char*)&(Session->RemoteLocationName
  670.                     .u.nbpEntity.objStr),MyNBPObjectStr[0] + 1);
  671.                 CopyData(&(MyNBPTypeStr[0]),(char*)&(Session->RemoteLocationName
  672.                     .u.nbpEntity.typeStr),MyNBPTypeStr[0] + 1);
  673.                 CopyData(&(MyNBPZoneStr[0]),(char*)&(Session->RemoteLocationName
  674.                     .u.nbpEntity.zoneStr),MyNBPZoneStr[0] + 1);
  675.                 Session->RemoteLocationName.locationKindSelector = ppcNBPLocation;
  676.                 ThePPCStartPBRec.locationName = &(Session->RemoteLocationName);
  677.             }
  678.          else
  679.             {
  680.                 if (PtrSize(MachineString) == 9)
  681.                     {
  682.                         long                            Scan;
  683.  
  684.                         for (Scan = 0; Scan < 9; Scan += 1)
  685.                             {
  686.                                 if ("localhost"[Scan] != MachineString[Scan])
  687.                                     {
  688.                                         goto RemoteName;
  689.                                     }
  690.                             }
  691.                     }
  692.                 /* it's the local machine */
  693.                 Session->RemoteLocationName.locationKindSelector = ppcNoLocation;
  694.                 ThePPCStartPBRec.locationName = &(Session->RemoteLocationName);
  695.             }
  696.         ThePPCStartPBRec.userData = 0; /* users are not used */
  697.         if (noErr != GetDefaultUser(&(ThePPCStartPBRec.userRefNum),UserNamePlace))
  698.             {
  699.                 ThePPCStartPBRec.userRefNum = 0;
  700.             }
  701.         TryCounter = MAXNUMTRIES;
  702.      TryAgainPoint:
  703.         Error = PPCStart(&ThePPCStartPBRec,False/*sync*/);
  704.         switch (Error)
  705.             {
  706.                 default:
  707.                     ReturnValue = eNetUnknownError;
  708.                     goto FailurePoint3;
  709.                 case destPortErr:
  710.                 case noResponseErr:
  711.                 case userRejectErr:
  712.                 case localOnlyErr:
  713.                 case guestNotAllowedErr:
  714.                     ReturnValue = eNetConnectRefused;
  715.                     goto FailurePoint3;
  716.                 case noErr:
  717.                     break; /* continue */
  718.                 case noInformErr:
  719.                     TryCounter -= 1;
  720.                     /* sleep a little bit */
  721.                     {
  722.                         double            Now;
  723.  
  724.                         Now = ReadTimer();
  725.                         while (TimerDifference(ReadTimer(),Now) < TRYDELAY)
  726.                             {
  727.                                 RelinquishCPUCheckCancel();
  728.                             }
  729.                     }
  730.                     /* see if we should try again */
  731.                     if (TryCounter > 0)
  732.                         {
  733.                             goto TryAgainPoint;
  734.                         }
  735.                     ReturnValue = eNetConnectRefused;
  736.                     goto FailurePoint3;
  737.                 case portClosedErr:
  738.                 case badPortNameErr:
  739.                 case noUserRecErr:
  740.                 case noPortErr:
  741.                 case badServiceMethodErr:
  742.                 case nameTypeErr:
  743.                     EXECUTE(PRERR(ForceAbort,"NetOpenSession:  bad error code"));
  744.                     break;
  745.             }
  746.         /* initialize all of the structure's parameters */
  747.         Session->SessionRefnum = ThePPCStartPBRec.sessRefNum;
  748.         Session->Port = AppleTalkSystemPort;
  749.         Session->WriteState = eWriteIdle;
  750.         Session->ReadState = eReadIdle;
  751.         Session->OverallState = eSessionNormal;
  752.         Session->SendHead = NIL;
  753.         Session->SendTail = NIL;
  754.         Session->SendInProgress = NIL;
  755.         Session->ReceiveHead = NIL;
  756.         Session->ReceiveTail = NIL;
  757.         Session->ReceiveInProgress = NIL;
  758.         *SessionOut = Session;
  759.         return eNetNoError;
  760.     }
  761.  
  762.  
  763. /* utility routine to decode string of format <name>:<type>@<zone> */
  764. /* we allow the omission of <type> and automatically substitute DEFAULTTYPE for it */
  765. /* you can omit the zone name as well. */
  766. static MyBoolean        DecodeMachineName(char* MachineStr, char ObjStr[32],
  767.                                             char TypeStr[32], char ZoneStr[32])
  768.     {
  769.         long                            Limit;
  770.         long                            Scan;
  771.  
  772.         Limit = PtrSize(MachineStr);
  773.         Scan = 0;
  774.         while ((Scan < Limit) && (MachineStr[Scan] != ':') && (MachineStr[Scan] != '@'))
  775.             {
  776.                 if (Scan >= 31)
  777.                     {
  778.                         return False;
  779.                     }
  780.                 ObjStr[Scan + 1] = MachineStr[Scan];
  781.                 Scan += 1;
  782.             }
  783.         ObjStr[0] = Scan;
  784.         if ((Scan == Limit) || (MachineStr[Scan] == '@'))
  785.             {
  786.                 /* type name omitted */
  787.                 CopyData(DEFAULTTYPE,&(TypeStr[1]),DEFAULTTYPELENGTH);
  788.                 TypeStr[0] = DEFAULTTYPELENGTH;
  789.                 goto ElidedTypePoint;
  790.             }
  791.         MachineStr += Scan + 1;
  792.         Limit -= Scan + 1;
  793.         Scan = 0;
  794.         while ((Scan < Limit) && (MachineStr[Scan] != '@'))
  795.             {
  796.                 if (Scan >= 31)
  797.                     {
  798.                         return False;
  799.                     }
  800.                 TypeStr[Scan + 1] = MachineStr[Scan];
  801.                 Scan += 1;
  802.             }
  803.         TypeStr[0] = Scan;
  804.      ElidedTypePoint:
  805.         if (Scan == Limit)
  806.             {
  807.                 /* no zone name */
  808.                 ZoneStr[0] = 0;
  809.                 return True;
  810.             }
  811.         MachineStr += Scan + 1;
  812.         Limit -= Scan + 1;
  813.         Scan = 0;
  814.         while (Scan < Limit)
  815.             {
  816.                 if (Scan >= 31)
  817.                     {
  818.                         return False;
  819.                     }
  820.                 ZoneStr[Scan + 1] = MachineStr[Scan];
  821.                 Scan += 1;
  822.             }
  823.         ZoneStr[0] = Scan;
  824.         return True;
  825.     }
  826.  
  827.  
  828. /* Close a session.  Any waiting data in either direction is discarded. */
  829. void                                NetCloseSession(SessionIDType* Session)
  830.     {
  831.         OSErr                            Error;
  832.         PPCEndPBRec                EndRec;
  833.         BufferRec*                BuffScan;
  834.  
  835.         ERROR(!Initialized,PRERR(ForceAbort,"NetCloseSession:  not initialized"));
  836.         CheckPtrExistence(Session);
  837.         /* first, kill the session to complete any reads/writes in progress. */
  838.         /* this might fail if the session is already dead. */
  839.         EndRec.sessRefNum = Session->SessionRefnum;
  840.         Error = PPCEnd(&EndRec,False/*sync*/);
  841.         /* now that that's finished, we can dump the buffers */
  842.         if (Session->ReceiveInProgress != NIL)
  843.             {
  844.                 ReleasePtr((char*)(Session->ReceiveInProgress));
  845.             }
  846.         if (Session->SendInProgress != NIL)
  847.             {
  848.                 ReleasePtr((char*)(Session->SendInProgress));
  849.             }
  850.         BuffScan = Session->SendHead;
  851.         while (BuffScan != NIL)
  852.             {
  853.                 BufferRec*                BuffTemp;
  854.  
  855.                 BuffTemp = BuffScan;
  856.                 BuffScan = BuffScan->Next;
  857.                 ReleasePtr((char*)BuffTemp);
  858.             }
  859.         BuffScan = Session->ReceiveHead;
  860.         while (BuffScan != NIL)
  861.             {
  862.                 BufferRec*                BuffTemp;
  863.  
  864.                 BuffTemp = BuffScan;
  865.                 BuffScan = BuffScan->Next;
  866.                 ReleasePtr((char*)BuffTemp);
  867.             }
  868.         /* now dump the session record */
  869.         ArrayDeleteElement(SessionArray,ArrayFindElement(SessionArray,Session));
  870.         ReleasePtr((char*)Session);
  871.     }
  872.  
  873.  
  874. /* utility routine to submit a new read operation if one has completed */
  875. static void                    UpdateRead(SessionIDType* Session)
  876.     {
  877.         OSErr                            Error;
  878.  
  879.         CheckPtrExistence(Session);
  880.         ERROR(Session->ReadState == eReadInProgress,PRERR(ForceAbort,
  881.             "UpdateRead:  read is already in progress"));
  882.         /* handle the data that comes back from a read operation. */
  883.         if (Session->ReadState == eReadFinished)
  884.             {
  885.                 CheckPtrExistence(Session->ReceiveInProgress);
  886.                 /* check for session closed notification */
  887.                 if (Session->ThePPCReadPBRec.ioResult == sessClosedErr)
  888.                     {
  889.                         Session->OverallState = eSessionClosed;
  890.                     }
  891.                 ERROR((Session->ThePPCReadPBRec.ioResult != sessClosedErr)
  892.                     && (Session->ThePPCReadPBRec.ioResult != noErr),PRERR(AllowResume,
  893.                     "UpdateRead:  error returned from PPCRead"));
  894.                 /* get read length */
  895.                 Session->ReceiveInProgress->NumBytes = Session->ThePPCReadPBRec.actualLength;
  896.                 ERROR((Session->ReceiveInProgress->NumBytes < 0)
  897.                     || (Session->ReceiveInProgress->NumBytes > BUFFERSIZE),PRERR(ForceAbort,
  898.                     "UpdateRead:  too many bytes returned from PPCRead"));
  899.                 if (Session->ReceiveInProgress->NumBytes != 0)
  900.                     {
  901.                         /* move this buffer onto the list */
  902.                         Session->ReceiveInProgress->Next = NIL;
  903.                         if (Session->ReceiveTail != NIL)
  904.                             {
  905.                                 CheckPtrExistence(Session->ReceiveTail);
  906.                                 Session->ReceiveTail->Next = Session->ReceiveInProgress;
  907.                             }
  908.                          else
  909.                             {
  910.                                 Session->ReceiveHead = Session->ReceiveInProgress;
  911.                             }
  912.                         Session->ReceiveTail = Session->ReceiveInProgress;
  913.                     }
  914.                  else
  915.                     {
  916.                         ReleasePtr((char*)(Session->ReceiveInProgress));
  917.                     }
  918.                 /* clear buffer register */
  919.                 Session->ReceiveInProgress = NIL;
  920.                 /* indicate that data has arrived */
  921.                 if (Session->OverallState == eSessionNormal)
  922.                     {
  923.                         /* don't change this if it contains eSessionClosed! */
  924.                         Session->OverallState = eSessionDataArrived;
  925.                     }
  926.                 /* reset read state */
  927.                 Session->ReadState = eReadIdle;
  928.             }
  929.         /* if the session has closed with the previous read, don't retrigger the read */
  930.         /* but instead just exit */
  931.         if (Session->OverallState == eSessionClosed)
  932.             {
  933.                 return;
  934.             }
  935.         /* retrigger the read operation on a new block */
  936.         Session->ReceiveInProgress = (BufferRec*)AllocPtrCanFail(
  937.             sizeof(BufferRec),"ReadBufferRec");
  938.         if (Session->ReceiveInProgress == NIL)
  939.             {
  940.                 return;
  941.             }
  942.         Session->ThePPCReadPBRec.ioCompletion = (ProcPtr)&MyPPCReadCompletionRoutine;
  943.         Session->ThePPCReadPBRec.sessRefNum = Session->SessionRefnum;
  944.         Session->ThePPCReadPBRec.bufferLength = BUFFERSIZE;
  945.         Session->ThePPCReadPBRec.bufferPtr = &(Session->ReceiveInProgress->Buffer[0]);
  946.         Session->ReadState = eReadInProgress; /* BEFORE the call! */
  947.         Error = PPCRead(&(Session->ThePPCReadPBRec),True/*async*/);
  948.     }
  949.  
  950.  
  951. /* utility routine to submit another write if one has completed */
  952. static void                    UpdateWrite(SessionIDType* Session)
  953.     {
  954.         OSErr                            Error;
  955.  
  956.         CheckPtrExistence(Session);
  957.         ERROR((Session->WriteState == eWriteInProgress),PRERR(ForceAbort,
  958.             "UpdateWrite:  write is already in progress"));
  959.         /* if a write has completed, then handle it */
  960.         if (Session->WriteState == eWriteFinished)
  961.             {
  962.                 /* check for session closed notification */
  963.                 if (Session->ThePPCWritePBRec.ioResult == sessClosedErr)
  964.                     {
  965.                         Session->OverallState = eSessionClosed;
  966.                     }
  967.                 ERROR((Session->ThePPCWritePBRec.ioResult != sessClosedErr)
  968.                     && (Session->ThePPCWritePBRec.ioResult != noErr),PRERR(AllowResume,
  969.                     "UpdateWrite:  error returned from PPCWrite"));
  970.                 /* reset write state stuff */
  971.                 Session->WriteState = eWriteIdle;
  972.                 /* dispose of the write buffer block */
  973.                 ReleasePtr((char*)(Session->SendInProgress));
  974.                 Session->SendInProgress = NIL;
  975.             }
  976.         /* if there is more data queued to be sent, then send it */
  977.         if (Session->SendHead != NIL)
  978.             {
  979.                 CheckPtrExistence(Session->SendHead);
  980.                 /* move the first block to the in progress register */
  981.                 Session->SendInProgress = Session->SendHead;
  982.                 /* pop the block off the queue */
  983.                 Session->SendHead = Session->SendHead->Next;
  984.                 if (Session->SendHead == NIL)
  985.                     {
  986.                         Session->SendTail = NIL;
  987.                     }
  988.                 /* construct the parameter record */
  989.                 Session->ThePPCWritePBRec.ioCompletion = (ProcPtr)&MyPPCWriteCompletionRoutine;
  990.                 Session->ThePPCWritePBRec.sessRefNum = Session->SessionRefnum;
  991.                 Session->ThePPCWritePBRec.bufferLength = Session->SendInProgress->NumBytes;
  992.                 Session->ThePPCWritePBRec.bufferPtr = &(Session->SendInProgress->Buffer[0]);
  993.                 Session->ThePPCWritePBRec.more = False; /* always false */
  994.                 Session->ThePPCWritePBRec.userData = 0;
  995.                 Session->ThePPCWritePBRec.blockCreator = 0;
  996.                 Session->ThePPCWritePBRec.blockType = 0;
  997.                 Session->WriteState = eWriteInProgress; /* BEFORE the call! */
  998.                 Error = PPCWrite(&(Session->ThePPCWritePBRec),True/*async*/);
  999.             }
  1000.     }
  1001.  
  1002.  
  1003. /* Verify that a session is still usable.  Returns True if the session is still */
  1004. /* available, or False if the remote system disconnected it.  If it returns False, */
  1005. /* then you should call NetCloseSession to dispose of the session record. */
  1006. MyBoolean                        NetIsSessionStillAlive(SessionIDType* Session)
  1007.     {
  1008.         ERROR(!Initialized,PRERR(ForceAbort,"NetIsSessionStillAlive:  not initialized"));
  1009.         CheckPtrExistence(Session);
  1010.         return (Session->OverallState != eSessionClosed);
  1011.     }
  1012.  
  1013.  
  1014. /* Obtain a string identifying the machine from which a session has been */
  1015. /* established.  The string is a non-null-terminated heap block. */
  1016. char*                                NetSessionGetRemoteMachineName(SessionIDType* Session)
  1017.     {
  1018.         char                            LocalBuffer[32 + 32 + 32 + 1 + 1];
  1019.         long                            Index;
  1020.         long                            Scan;
  1021.         char*                            Pointer;
  1022.  
  1023.         ERROR(!Initialized,PRERR(ForceAbort,
  1024.             "NetSessionGetRemoteMachineName:  not initialized"));
  1025.         CheckPtrExistence(Session);
  1026.         switch (Session->RemoteLocationName.locationKindSelector)
  1027.             {
  1028.                 default:
  1029.                     EXECUTE(PRERR(ForceAbort,
  1030.                         "NetSessionGetRemoteMachineName:  unknown locationKindSelector"));
  1031.                     break;
  1032.                 case ppcNoLocation:
  1033.                     Index = 9;
  1034.                     CopyData("localhost",LocalBuffer,9);
  1035.                     break;
  1036.                 case ppcNBPLocation:
  1037.                     Index = 0;
  1038.                     for (Scan = 0; Scan < Session->RemoteLocationName
  1039.                         .u.nbpEntity.objStr[0]; Scan += 1)
  1040.                         {
  1041.                             LocalBuffer[Index++] = Session->RemoteLocationName
  1042.                                 .u.nbpEntity.objStr[Scan + 1];
  1043.                         }
  1044.                     if (Session->RemoteLocationName.u.nbpEntity.typeStr[0] != 0)
  1045.                         {
  1046.                             LocalBuffer[Index++] = ':';
  1047.                         }
  1048.                     for (Scan = 0; Scan < Session->RemoteLocationName
  1049.                         .u.nbpEntity.typeStr[0]; Scan += 1)
  1050.                         {
  1051.                             LocalBuffer[Index++] = Session->RemoteLocationName
  1052.                                 .u.nbpEntity.typeStr[Scan + 1];
  1053.                         }
  1054.                     if (Session->RemoteLocationName.u.nbpEntity.zoneStr[0] != 0)
  1055.                         {
  1056.                             LocalBuffer[Index++] = '@';
  1057.                         }
  1058.                     for (Scan = 0; Scan < Session->RemoteLocationName
  1059.                         .u.nbpEntity.zoneStr[0]; Scan += 1)
  1060.                         {
  1061.                             LocalBuffer[Index++] = Session->RemoteLocationName
  1062.                                 .u.nbpEntity.zoneStr[Scan + 1];
  1063.                         }
  1064.                     break;
  1065.             }
  1066.         Pointer = AllocPtrCanFail(Index,"NBPNameString");
  1067.         if (Pointer != NIL)
  1068.             {
  1069.                 CopyData(&(LocalBuffer[0]),&(Pointer[0]),Index);
  1070.             }
  1071.         return Pointer;
  1072.     }
  1073.  
  1074.  
  1075. /* Find out how much data is waiting to be read from the port. */
  1076. long                                NetHowMuchDataToRead(SessionIDType* Session)
  1077.     {
  1078.         long                            DataCount;
  1079.         BufferRec*                BufferScan;
  1080.  
  1081.         ERROR(!Initialized,PRERR(ForceAbort,"NetHowMuchDataToRead:  not initialized"));
  1082.         CheckPtrExistence(Session);
  1083.         BufferScan = Session->ReceiveHead;
  1084.         DataCount = 0;
  1085.         while (BufferScan != NIL)
  1086.             {
  1087.                 DataCount += BufferScan->NumBytes;
  1088.                 BufferScan = BufferScan->Next;
  1089.             }
  1090.         return DataCount;
  1091.     }
  1092.  
  1093.  
  1094. /* Find out how much data is waiting in local buffers to be written to a port. */
  1095. long                                NetHowMuchDataToWrite(SessionIDType* Session)
  1096.     {
  1097.         long                            DataCount;
  1098.         BufferRec*                BufferScan;
  1099.  
  1100.         ERROR(!Initialized,PRERR(ForceAbort,"NetHowMuchDataToWrite:  not initialized"));
  1101.         CheckPtrExistence(Session);
  1102.         /* initiate another read if there isn't one pending */
  1103.         if (Session->ReadState == eReadIdle)
  1104.             {
  1105.                 UpdateRead(Session);
  1106.             }
  1107.         /* count data in buffers waiting to be eaten */
  1108.         BufferScan = Session->SendHead;
  1109.         DataCount = 0;
  1110.         while (BufferScan != NIL)
  1111.             {
  1112.                 DataCount += BufferScan->NumBytes;
  1113.                 BufferScan = BufferScan->Next;
  1114.             }
  1115.         return DataCount;
  1116.     }
  1117.  
  1118.  
  1119. /* Read data from a session.  It is an error to read more data than there is waiting. */
  1120. void                                NetReadData(SessionIDType* Session, char* Buffer, long NumBytes)
  1121.     {
  1122.         ERROR(!Initialized,PRERR(ForceAbort,"NetReadData:  not initialized"));
  1123.         CheckPtrExistence(Session);
  1124.         ERROR((NumBytes < 0) || (NumBytes > NetHowMuchDataToRead(Session)),
  1125.             PRERR(ForceAbort,"NetReadData:  number of bytes is out of range"));
  1126.         /* extract data from blocks */
  1127.         while (NumBytes > 0)
  1128.             {
  1129.                 long                            TempNumBytes;
  1130.                 BufferRec*                TempBuffer;
  1131.  
  1132.                 TempBuffer = Session->ReceiveHead;
  1133.                 CheckPtrExistence(TempBuffer);
  1134.                 TempNumBytes = TempBuffer->NumBytes;
  1135.                 CopyData(&(TempBuffer->Buffer[0]),&(Buffer[0]),TempNumBytes);
  1136.                 Session->ReceiveHead = TempBuffer->Next;
  1137.                 NumBytes -= TempNumBytes;
  1138.                 Buffer += TempNumBytes;
  1139.                 ReleasePtr((char*)TempBuffer);
  1140.             }
  1141.         if (Session->ReceiveHead == NIL)
  1142.             {
  1143.                 Session->ReceiveTail = NIL;
  1144.             }
  1145.         /* initiate another read if there isn't one pending */
  1146.         if (Session->ReadState == eReadIdle)
  1147.             {
  1148.                 UpdateRead(Session);
  1149.             }
  1150.     }
  1151.  
  1152.  
  1153. /* Write data to a session.  If data could not be sent without blocking, then */
  1154. /* it is locally buffered until it can be sent (that's what NetUpdate is for) */
  1155. /* returns True if successful, or False if there isn't enough memory.  if it fails, */
  1156. /* then NO data is written (i.e. data is never partially written) */
  1157. MyBoolean                        NetWriteData(SessionIDType* Session, char* Buffer, long NumBytes)
  1158.     {
  1159.         BufferRec*                LocalHead;
  1160.         BufferRec*                LocalTail;
  1161.         BufferRec*                BuffScan;
  1162.         long                            TempByteCount;
  1163.  
  1164.         ERROR(!Initialized,PRERR(ForceAbort,"NetWriteData:  not initialized"));
  1165.         CheckPtrExistence(Session);
  1166.         /* we create the buffers totally first, and only submit them if we could */
  1167.         /* allocate ALL of them */
  1168.         LocalHead = NIL;
  1169.         LocalTail = NIL;
  1170.         TempByteCount = NumBytes;
  1171.         while (TempByteCount > 0)
  1172.             {
  1173.                 BufferRec*                TempBuff;
  1174.  
  1175.                 if (TempByteCount > BUFFERSIZE)
  1176.                     {
  1177.                         TempByteCount -= BUFFERSIZE;
  1178.                     }
  1179.                  else
  1180.                     {
  1181.                         TempByteCount -= TempByteCount;
  1182.                     }
  1183.                 TempBuff = (BufferRec*)AllocPtrCanFail(sizeof(BufferRec),"BufferRec");
  1184.                 if (TempBuff == NIL)
  1185.                     {
  1186.                         while (LocalHead != NIL)
  1187.                             {
  1188.                                 /* dump ones that we already allocated */
  1189.                                 CheckPtrExistence(LocalHead);
  1190.                                 TempBuff = LocalHead;
  1191.                                 LocalHead = LocalHead->Next;
  1192.                                 ReleasePtr((char*)TempBuff);
  1193.                             }
  1194.                         return False; /* oops */
  1195.                     }
  1196.                 TempBuff->Next = NIL;
  1197.                 if (LocalTail != NIL)
  1198.                     {
  1199.                         LocalTail->Next = TempBuff;
  1200.                     }
  1201.                  else
  1202.                     {
  1203.                         LocalHead = TempBuff;
  1204.                     }
  1205.                 LocalTail = TempBuff;
  1206.             }
  1207.         /* buffers allocated, now fill them */
  1208.         TempByteCount = NumBytes;
  1209.         BuffScan = LocalHead;
  1210.         while (TempByteCount > 0)
  1211.             {
  1212.                 long                            NumBytesThisTime;
  1213.  
  1214.                 if (TempByteCount > BUFFERSIZE)
  1215.                     {
  1216.                         NumBytesThisTime = BUFFERSIZE;
  1217.                     }
  1218.                  else
  1219.                     {
  1220.                         NumBytesThisTime = TempByteCount;
  1221.                     }
  1222.                 CheckPtrExistence(BuffScan);
  1223.                 BuffScan->NumBytes = NumBytesThisTime;
  1224.                 CopyData(&(Buffer[0]),&(BuffScan->Buffer[0]),NumBytesThisTime);
  1225.                 Buffer += NumBytesThisTime;
  1226.                 TempByteCount -= NumBytesThisTime;
  1227.                 BuffScan = BuffScan->Next;
  1228.             }
  1229.         ERROR(BuffScan != NIL,PRERR(ForceAbort,
  1230.             "NetWriteData:  BuffScan not NIL after buffer fill loop"));
  1231.         /* now, tack it on the end of the write buffer list */
  1232.         if (Session->SendTail != NIL)
  1233.             {
  1234.                 Session->SendTail->Next = LocalHead;
  1235.             }
  1236.          else
  1237.             {
  1238.                 Session->SendHead = LocalHead;
  1239.             }
  1240.         Session->SendTail = LocalTail;
  1241.         /* if there is no outstanding write, then make a write */
  1242.         if (Session->WriteState == eWriteIdle)
  1243.             {
  1244.                 UpdateWrite(Session);
  1245.             }
  1246.         /* successful */
  1247.         return True;
  1248.     }
  1249.  
  1250.  
  1251. #ifdef THINK_C
  1252.     #if __option(profile)
  1253.         #define Profiling (True)
  1254.     #else
  1255.         #define Profiling (False)
  1256.     #endif
  1257.  
  1258.     #pragma options(!profile)
  1259. #endif
  1260.  
  1261. static pascal void    MyPPCInformCompletionRoutine(PPCInformPBRec* PB)
  1262.     {
  1263.         PortIDType*                Port;
  1264.  
  1265.         /* some goofy pointer arithmetic to get from a member of the struct */
  1266.         /* back to the beginning of the struct. */
  1267.         Port = (PortIDType*)((char*)PB - (long)&(((PortIDType*)NIL)->ThePPCInformPBRec));
  1268.         /* now we can access the other members */
  1269.         Port->PortState = eConnectionPending; /* indicate that PPCInform is done. */
  1270.     }
  1271.  
  1272. static pascal void    MyPPCReadCompletionRoutine(PPCParamBlockRec* PB)
  1273.     {
  1274.         SessionIDType*        Session;
  1275.  
  1276.         /* some goofy pointer arithmetic to get from a member of the struct */
  1277.         /* back to the beginning of the struct. */
  1278.         Session = (SessionIDType*)((char*)PB
  1279.             - (long)&(((SessionIDType*)NIL)->ThePPCReadPBRec));
  1280.         /* now we can access the other members */
  1281.         Session->ReadState = eReadFinished;
  1282.     }
  1283.  
  1284. static pascal void    MyPPCWriteCompletionRoutine(PPCParamBlockRec* PB)
  1285.     {
  1286.         SessionIDType*        Session;
  1287.  
  1288.         /* some goofy pointer arithmetic to get from a member of the struct */
  1289.         /* back to the beginning of the struct. */
  1290.         Session = (SessionIDType*)((char*)PB
  1291.             - (long)&(((SessionIDType*)NIL)->ThePPCWritePBRec));
  1292.         /* now we can access the other members */
  1293.         Session->WriteState = eWriteFinished;
  1294.     }
  1295.  
  1296. #ifdef THINK_C
  1297.     #if Profiling
  1298.         #pragma options(profile)
  1299.     #endif
  1300. #endif
  1301.